import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

import 'package:hex/hex.dart';
import 'package:http/http.dart' as http;
import 'package:lib_database/lib_database.dart';
import 'package:lib_util/utils/utils.dart';
import 'package:solana_web3/programs.dart';
import 'package:solana_web3/solana_web3.dart' as web3;
import 'package:solana_web3/solana_web3.dart';
class SolanaWeb3 {
  // 获取 tron 区块高度
  static Future<int> getBlockHeightByRpc(String rpcUrl) async {
    try {
      var response = await SolanaWeb3.createRpcRequest(
          rpc: rpcUrl,
          body: {"jsonrpc": "2.0", "id": 1, "method": "getBlockHeight"});
      return int.parse(response['result'].toString());
    } catch (e) {
      print('getBlockHeightByRpc error::::: $e');
      // return 0;
      throw Exception('getBlockHeightByRpc error:::::  $e');
    }
  }

  // 获取 主 币 余额  存在 精度问题
  static Future<String> getMainTokenBalance(String rpcUrl, String userAddress,
      {int decimal = 9}) async {
    try {
      var response = await SolanaWeb3.createRpcRequest(rpc: rpcUrl, body: {
        "jsonrpc": "2.0",
        "id": 2,
        "method": "getBalance",
        "params": [userAddress]
      });
      print('getMainTokenBalance ::::: $response');
      return Utils.formatStringBalance(
          (BigInt.parse(response['result']['value'].toString()) /
                  BigInt.from(10).pow(decimal))
              .toString(),
          fractionDigits: Config.balanceFixedNum);
    } catch (e) {
      print('getMainTokenBalance error::::: $e');
      return '0';
    }
  }

  // 获取 代币 余额
  static Future<String> getTokenBalance(String rpcUrl, String tokenAddress,String userAddress,
      {int decimals = 9}) async {
    try {
      Map<String, dynamic> requestBody = {
        "jsonrpc": "2.0",
        "id": 3,
        "method": "getTokenAccountsByOwner",
        "params": [
          userAddress,
          {"mint": tokenAddress},
          {"encoding": "jsonParsed"}
        ]
      };
      var response =
      await SolanaWeb3.createRpcRequest(rpc: rpcUrl, body: requestBody);
      return response['result']['value'][0]['account']['data']['parsed']['info']['tokenAmount']['uiAmountString'];
    } catch (e) {
      print('getTokenBalance error::::: $e');
      return '0';
    }
  }

  // 获取 以太 gas value为 int 类型 ，
  static Future<Map<String, dynamic>?> getErc20Gas(rpcUrl, String? content,
      {int decimal = 9}) async {
    try {
      var allGas = await SolanaWeb3.createRpcRequest(rpc: rpcUrl, body: {
        "jsonrpc": "2.0",
        "id": 1,
        "method": "getRecentPrioritizationFees",
        "params": [[]]
      });
      final theMaxGas = allGas['result'].reduce((item, element) =>
          item['prioritizationFee'] > element['prioritizationFee']
              ? item
              : element);
      // final gas = double.parse(Utils.formatStringBalance(
      //     (BigInt.parse(theMaxGas['prioritizationFee'].toString()) / BigInt.from(10).pow(decimal))
      //         .toString(),
      //     fractionDigits: Config.balanceFixedNum));
      // var version = await SolanaWeb3.createRpcRequest(rpc: rpcUrl, body:  {"jsonrpc":"2.0","id":101, "method":"getVersion"});
      // if(int.parse(version['result']['solana-core'].toString().split('.')[0]) <= 2){
      //   var response = await SolanaWeb3.createRpcRequest(rpc : rpcUrl, body: { "jsonrpc":"2.0", "id": 109, "method":"getFees"});
      //   final value=response['result']['value']['feeCalculator']['lamportsPerSignature'].toString();
      //   String gas =Utils.formatStringBalance((BigInt.parse(value) / BigInt.from(10).pow(decimal)).toString(),
      //       fractionDigits: Config.balanceFixedNum);
      //   return {'gasPrice': gas, 'gas': gas, "adjustedGas": gas};
      // }
      // String base64Encoded = content!=null?base64.encode(utf8.encode(content)):'AQABAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQAA';
      // print('base64Encoded: $base64Encoded');
      // // 1. 连接以太坊节点
      // var response = await SolanaWeb3.createRpcRequest(rpc : rpcUrl, body:   {
      //   "jsonrpc": "2.0",
      //   "id": 110,
      //   "method": "getFeeForMessage",
      //   "params": [
      //     base64Encoded,
      //     {
      //       "commitment":"processed"
      //     }
      //   ]
      // });
      // final value=response['result']['value'].toString();
      // String gas =Utils.formatStringBalance((BigInt.parse(value) / BigInt.from(10).pow(decimal)).toString(),
      //     fractionDigits: Config.balanceFixedNum);
      // print({
      //   'gasPrice': '1',
      //   'gas': theMaxGas['prioritizationFee'].toString(),
      //   "adjustedGas": theMaxGas['prioritizationFee'].toString()
      // });
      return {
        'gasPrice': '1',
        'gas': theMaxGas['prioritizationFee'].toString(),
        "adjustedGas": theMaxGas['prioritizationFee'].toString()
      };
      return {'gas': '1'};
    } catch (e) {
      throw Exception(e.toString());
    }
  }

  // 新增代币 币种信息
  static Future<Map<String, dynamic>?> getERC20TokenInfoByRpc(
    String rpcUrl, {
    required String accountAddress, // 主链钱包地址
    required String tokenMintAddress, // 指定的 Token 的 Mint 地址
  }) async {
    try {
      Map<String, dynamic> requestBody = {
        "jsonrpc": "2.0",
        "id": 1233,
        "method": "getTokenAccountsByOwner",
        "params": [
          accountAddress,
          {"mint": tokenMintAddress},
          {"encoding": "jsonParsed"}
        ]
      };
      var result =
          await SolanaWeb3.createRpcRequest(rpc: rpcUrl, body: requestBody);
      final response = result['result']['value'];
      //  没数据情况下 是空的
      if (response == [] || response.isEmpty) {
        //   申请 创建
        //   用户地址 找不到
        return {};
      }
      // final resultOne=
      final decimals = response[0]['account']['data']['parsed']['info']
          ['tokenAmount']['decimals'];
      // 检查帐户是否持有指定的 Token
      return {
        "chainId": 95279725,
        "address": tokenMintAddress,
        "name": '',
        "decimals": int.parse(decimals.toString()),
        "symbol": '',
      };
    } catch (e) {
      print('getERC20TokenInfoByRpc error::::: $e');
      return null;
    }
  }

  // 签名solana
  static Future<Map<String, dynamic>> getWeb3SignTx(
      rpcUrl, int chainId, Map<String, dynamic> tran, tokenAddress, type) async {
    final connection = web3.Connection(Cluster(Uri.parse(rpcUrl)));
    try{
      //       Map<String, dynamic> tran = {
      //         "from": ethTransaction.from,
      //         "to": ethTransaction.to,
      //         "value": ethTransaction.value,
      //         "gas": ethTransaction.gas,
      //         "gasPrice": ethTransaction.gasPrice,
      //         "nonce": ethTransaction.nonce,
      //         "data": ethTransaction.data,
      //       };
      var fromPubKey = Pubkey.fromString(tran['from']);
      var lamports = BigInt.parse(tran['value'].toString());
      var recipientAccount= Pubkey.fromString(tran['to']);
      late web3.Transaction rawTransaction;
      if(type == TokenType.token20.name){
       var mint = Pubkey.fromString(tokenAddress);
       var fromAddress =
       await SolanaWeb3.createRpcRequest(rpc: rpcUrl, body: {
         "jsonrpc": "2.0",
         "id": 1233,
         "method": "getTokenAccountsByOwner",
         "params": [
           tran['from'],
           {"mint": tokenAddress},
           {"encoding": "jsonParsed"}
         ]
       });
       var basePubkey=  Pubkey.fromString(fromAddress['result']['value'][0]['pubkey']);
       var toAddress =
       await SolanaWeb3.createRpcRequest(rpc: rpcUrl, body: {
         "jsonrpc": "2.0",
         "id": 123345,
         "method": "getTokenAccountsByOwner",
         "params": [
           tran['to'],
           {"mint": tokenAddress},
           {"encoding": "jsonParsed"}
         ]
       });
       if(toAddress['result']['value'].length <= 0){
         throw 'The recipient does not have an account associated currency address！';
       }
       var toTokenAddress=  Pubkey.fromString(toAddress['result']['value'][0]['pubkey']);
       var decimals=  toAddress['result']['value'][0]['account']['data']['parsed']['info']['tokenAmount']['decimals'];
       final BlockhashWithExpiryBlockHeight blockHash = await connection.getLatestBlockhash();
       rawTransaction = web3.Transaction.legacy(
           payer: fromPubKey,
           recentBlockhash: blockHash.blockhash,
           instructions: [
             TokenProgram.transferChecked(
                 source: basePubkey,
                 destination: toTokenAddress,
                 owner: fromPubKey,
                 amount: lamports,
                 mint: mint,
                 decimals: int.parse(decimals.toString())
              )
           ]
       );
      }else{
        final BlockhashWithExpiryBlockHeight blockHash = await connection.getLatestBlockhash();
        rawTransaction = web3.Transaction.legacy(
            payer: fromPubKey,
            recentBlockhash: blockHash.blockhash,
            instructions: [
              SystemProgram.transfer(
                fromPubkey: fromPubKey,
                toPubkey: recipientAccount,
                lamports: lamports,
              ),
            ]
        );
      }
      final rawTransactionSE = rawTransaction.message.serialize();
      List<int> intList = rawTransactionSE.toList();
      var tx= HEX.encode(intList);
      Map<String, dynamic> data = {
        "tran": rawTransaction,
        'tx': tx,
      };
      return data;
    }catch(e){
      throw e.toString();
    }
  }

  // 拿 MPC 签名后的 内容 进行 转账交易
  static Future<String?> ethSignSendTransaction(String rpcUrl, int chainId,Transaction txInfo, String signInfo,
      {bool status = false}) async {
    final connection = web3.Connection(Cluster(Uri.parse(rpcUrl)));
    try {
      final signatureInfo = HEX.decode(signInfo);
      Uint8List signature = Uint8List.fromList(signatureInfo);
      final Transaction manualTransaction = Transaction(signatures: [signature], message: txInfo.message);
      final resp= await connection.sendTransactionRaw(manualTransaction);
      final transactionHash= resp.result;
      connection.dispose();
      // if (status)
      //   localTranData(rpcUrl, transactionHash, txInfo);
      return transactionHash;
    } catch (e) {
      connection.dispose();
      throw e.toString();
    }
  }


  //交易成功后修改缓存数据
  static Future<Map<String,dynamic>>  solTransactionDetail({
    required String rpc,
    required String txHash,
}) async {
    final connection = web3.Connection(Cluster(Uri.parse(rpc)));
    try {
      // 获取  区块信息 交易费用
      var transactionInfo = await connection.getTransaction(txHash,config: GetTransactionConfig(
          encoding:TransactionEncoding.jsonParsed
      ));
      print('solTransactionDetail::::::: $transactionInfo');
      if(transactionInfo == null){
        throw Exception('Not found transactionInfo');
      }
      // if (data is TransactionReceipt) {
      final gas = BigInt.from(int.parse(transactionInfo.meta!.fee.toString())) / BigInt.from(10).pow(9);
      final txFee = Utils.formatStringBalance((gas).toString(),
          fractionDigits: 9, trimDecimalZero: true);
      // 获取 gas 的费用
      final slot = transactionInfo.slot;
      //   //receipt,上链了,并且链上执行成功
      print('txFee ::::::: $txFee');
      print('block :::::: ${slot.toString()}');
      print('confirmed :::::: ${(Utils.isNotEmpty(txFee) && Utils.isNotEmpty(slot.toString()) && (slot != 0))}');
      // }
      connection.dispose();
      return {
        "gasFee": txFee,
        "status": '1',
        "block": slot.toString(),
        "confirmed": (Utils.isNotEmpty(txFee) && Utils.isNotEmpty(slot.toString()) && (slot != 0))
      };
    } catch (err) {
      connection.dispose();
      throw err.toString();
    }
  }

  //交易成功后修改缓存数据
  static localTranData(String rpc,hash,Transaction txInfo) async {
    final web3Connect = web3.Connection(Cluster(Uri.parse(rpc)));
    try {
      // 获取  区块信息 交易费用
      var transactionInfo = await web3Connect.getTransaction(hash,config: GetTransactionConfig(
        encoding:TransactionEncoding.jsonParsed
      ));
      print('transactionInfo：：：：：： $transactionInfo');
      if(transactionInfo == null){
        ObjectBox().transactionModel.updatePendingTransactionState2(
            TransactionBean(txId: hash, state: 2.toString(), pending: true));
        return;
      }
      // if (data is TransactionReceipt) {
      final gas = BigInt.from(int.parse(transactionInfo.meta!.fee.toString())) / BigInt.from(10).pow(9);
      final txFee = Utils.formatStringBalance((gas).toString(),
      fractionDigits: 9, trimDecimalZero: true);
      // 获取 gas 的费用
      final slot = transactionInfo.slot;
      web3Connect.dispose();
      //   //receipt,上链了,并且链上执行成功
      ObjectBox().transactionModel.updatePendingTransactionState2(
            TransactionBean(txId: hash, height: slot.toString(), txFee: txFee, state:  1.toString(), pending: false));
      // }
    } catch (err) {
      web3Connect.dispose();
      print('localTranData :::: $err');
      return null;
    }
  }

  static Future<Map<String, dynamic>> createRpcRequest(
      {required String rpc,
      required Map<String, dynamic> body,
      String method = 'post'}) async {
    late http.Response resp;
    if (method == 'post') {
      resp = await http.post(Uri.parse(rpc),
          headers: {
            HttpHeaders.contentTypeHeader: 'application/json',
            HttpHeaders.acceptHeader: "application/json"
          },
          body: json.encode(body));
    } else {
      resp = await http.get(Uri.parse(rpc));
    }

    if (resp.statusCode == 308) {
      //post 重定向
      resp = await http.post(Uri.parse(resp.headers["location"]!),
          headers: {
            HttpHeaders.contentTypeHeader: 'application/json',
            HttpHeaders.acceptHeader: "application/json"
          },
          body: json.encode(body));
    }
    String result = utf8.decode(resp.bodyBytes);
    Config.logger.d('rpc request:\n${json.encode(body)}\nresponse:\n$result');
    Map<String, dynamic> map = json.decode(result);
    return map;
  }
}
